<?php
namespace app\common;

class ParseCrontab {

    /**
     *  解析crontab的定时格式，linux只支持到分钟/，这个类支持到秒
     * @param array $crontab :
     *
     *  1    2    3    4    5
     *  *    *    *    *    *
     *  -    -    -    -    -
     *  |    |    |    |    |
     *  |    |    |    |    +----- day of week (0 - 6) (Sunday=0)
     *  |    |    |    +----- month (1 - 12)
     *  |    |    +------- day of month (1 - 31)
     *  |    +--------- hour (0 - 23)
     *  +----------- min (0 - 59)
     * @param int $start_timestamp timestamp [default=current timestamp]
     * @return array 下一分钟需要执行任务的时间点,如果没有的返回空
     */
    static public function parse($crontab, $start_timestamp) {
        $minutes = self::parse_cron_number($crontab['minute'], 0, 59); //当前的分钟数
        $hours = self::parse_cron_number($crontab['hour'], 0, 23); //小时，24 小时格式，没有前导零 0 到 23 
        $days = self::parse_cron_number($crontab['day'], 1, 31); // 月份中的第几天，没有前导零 1 到 31
        $months = self::parse_cron_number($crontab['month'], 1, 12); //数字表示的月份，没有前导零 1 到 12
        $weeks = self::parse_cron_number($crontab['week'], 0, 6); //星期中的第几天，数字表示 0（表示星期天）到 6（表示星期六）
        if (
            in_array(intval(date('i', $start_timestamp)), $minutes) &&
            in_array(intval(date('G', $start_timestamp)), $hours) &&
            in_array(intval(date('j', $start_timestamp)), $days) &&
            in_array(intval(date('n', $start_timestamp)), $months) &&
            in_array(intval(date('w', $start_timestamp)), $weeks)
        ) {
            return $start_timestamp;
        }
        return FALSE;
    }

    /**
     * 解析crontab表达式中单个配置
     * @param string $str  
     * @param int $min  单个配置对应的最小值
     * @param int $max  单个配置对应的最大值
     * @return array    
     */
    static public function parse_cron_number($str, $min, $max) {
        $result = [];
        $cron_arr = explode(',', $str);
        foreach ($cron_arr as $unit) {
            if (is_numeric($unit) && $unit >= $min && $unit <= $max) {
                $result[] = $unit;
                continue;
            }
            $ds_unit = explode('/', $unit);
            $step = empty($ds_unit[1]) ? 1 : $ds_unit[1];
            if ($ds_unit[0] !== '*') {
                $mix_max = explode('-', $ds_unit[0]);
                $min = min($mix_max) < $min ? $min : min($mix_max);
                $max = max($mix_max) > $max ? $max : max($mix_max);
            }
            for ($i = $min; $i <= $max; $i += $step) {
                $result[] = intval($i);
            }
        }
        return array_unique($result);
    }

    static public function currentTime() {
        return date('Y-m-d H:i:s').'测试';
    }

}
